package controllers

import (
	"bytes"
	"context"
	"github.com/filecoin-project/go-address"
	"github.com/milkbobo/fishgoweb/language"
	"github.com/tealeg/xlsx/v3"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo/options"
	"miners-monitor/models/client"
	"miners-monitor/models/common"
	"miners-monitor/models/expend"
	"miners-monitor/models/income"
	"miners-monitor/models/miner"
	"miners-monitor/models/total"
	"time"
)

type MinerController struct {
	BaseController
	CommonFunc       common.CommonFunc
	ClientAo         client.ClientAoModel
	MinerAo          miner.MinerAoModel
	TotalAo          total.TotalAoModel
	DefineController DefineController
	ExpendAo         expend.ExpendAoModel
	IncomeAo         income.IncomeAoModel
}

func (this *MinerController) Search_Json() interface{} {
	filterData := miner.MinerBase{
		MinerId: this.Ctx.GetParam("MinerId"),
	}

	this.ClientAo.IsLogin()
	filter, err := this.CommonFunc.StructToBsonD(filterData)
	if err != nil {
		panic(err)
	}

	result := this.MinerAo.Search(filter)
	for key, singleMiner := range result.Data {
		result.Data[key].IsSyncRoundWinner = total.SyncRoundWinnerMap[singleMiner.MinerId] > 0
	}
	return result
}

func (this *MinerController) SearchExpend_Json() interface{} {
	opts := this.CommonFunc.GetPaging()
	result := this.searchExpend(opts)
	result.IsSync = IsSyncExpend
	return result
}

func (this *MinerController) SearchIncome_Json() interface{} {
	opts := this.CommonFunc.GetPaging()
	result := this.searchIncome(opts)
	result.IsSync = IsSyncIncome
	return result
}

func (this *MinerController) ExpendExport_Raw() interface{} {
	result := this.searchExpend(options.Find())
	file := xlsx.NewFile()
	sheet, err := file.AddSheet("Sheet1")
	if err != nil {
		panic(err)
	}
	row := sheet.AddRow()
	row.SetHeight(20)
	cell := row.AddCell()
	cell.SetString("矿工号")
	cell = row.AddCell()
	cell.SetString("日期")
	cell = row.AddCell()
	cell.SetString("消耗FIL")
	cell = row.AddCell()
	cell.SetString("抵押")
	cell = row.AddCell()
	cell.SetString("Gas")
	cell = row.AddCell()
	cell.SetString("密封算力")
	cell = row.AddCell()
	cell.SetString("密封成本")
	cell = row.AddCell()
	cell.SetString("抵押成本")
	cell = row.AddCell()
	cell.SetString("Gas成本")
	cell = row.AddCell()
	cell.SetString("罚币")
	for _, transfersList2 := range result.Data {
		row := sheet.AddRow()
		row.SetHeight(20)
		cell := row.AddCell()
		cell.SetString(transfersList2.MinerId)
		cell = row.AddCell()
		cell.SetString(transfersList2.TimeString)
		cell = row.AddCell()
		cell.SetFloat(transfersList2.ConsumeBalances)
		cell = row.AddCell()
		cell.SetFloat(transfersList2.Pledge)
		cell = row.AddCell()
		cell.SetFloat(transfersList2.Gas)
		cell = row.AddCell()
		cell.SetFloat(transfersList2.PowerGrowth)
		cell = row.AddCell()
		cell.SetFloat(transfersList2.CostPerTib)
		cell = row.AddCell()
		cell.SetFloat(transfersList2.CostPerTibByGas)
		cell = row.AddCell()
		cell.SetFloat(transfersList2.CostPerTibByPledge)
		cell = row.AddCell()
		cell.SetFloat(transfersList2.Penalty)
	}

	var b bytes.Buffer //直接定义一个 Buffer 变量，而不用初始化
	file.Write(&b)
	this.Ctx.WriteMimeHeader("xlsx", "expend")
	return b.Bytes()
}

func (this *MinerController) IncomeExport_Raw() interface{} {
	result := this.searchIncome(options.Find())
	file := xlsx.NewFile()
	sheet, err := file.AddSheet("Sheet1")
	if err != nil {
		panic(err)
	}
	row := sheet.AddRow()
	row.SetHeight(20)
	cell := row.AddCell()
	cell.SetString("矿工号")
	cell = row.AddCell()
	cell.SetString("日期")
	cell = row.AddCell()
	cell.SetString("出块权")
	cell = row.AddCell()
	cell.SetString("出块数")
	cell = row.AddCell()
	cell.SetString("赢块率")
	cell = row.AddCell()
	cell.SetString("幸运值")
	cell = row.AddCell()
	cell.SetString("出块奖励")
	cell = row.AddCell()
	cell.SetString("出块收益")
	cell = row.AddCell()
	cell.SetString("释放FIL")
	cell = row.AddCell()
	cell.SetString("立即释放")
	cell = row.AddCell()
	cell.SetString("线性释放")
	for _, transfersList2 := range result.Data {
		row := sheet.AddRow()
		row.SetHeight(20)
		cell := row.AddCell()
		cell.SetString(transfersList2.MinerId)
		cell = row.AddCell()
		cell.SetString(transfersList2.TimeString)
		cell = row.AddCell()
		if transfersList2.RoundWinner < 0 {
			cell.SetString("-")
		} else {
			cell.SetInt(int(transfersList2.RoundWinner))
		}
		cell = row.AddCell()
		cell.SetInt(transfersList2.RewardCount)
		cell = row.AddCell()
		cell.SetFloat(transfersList2.WinPerDay)
		cell = row.AddCell()
		cell.SetFloat(transfersList2.Lucky)
		cell = row.AddCell()
		cell.SetFloat(transfersList2.RewardFil)
		cell = row.AddCell()
		cell.SetFloat(transfersList2.EveryTibEarnings)
		cell = row.AddCell()
		cell.SetFloat(transfersList2.ReleaseFil)
		cell = row.AddCell()
		cell.SetFloat(transfersList2.ImmediateRelease)
		cell = row.AddCell()
		cell.SetFloat(transfersList2.Vesting)
	}

	var b bytes.Buffer //直接定义一个 Buffer 变量，而不用初始化
	file.Write(&b)
	this.Ctx.WriteMimeHeader("xlsx", "expend")
	return b.Bytes()
}

func (this *MinerController) searchExpend(opts *options.FindOptions) expend.Expends {

	client := this.ClientAo.IsLogin()
	filter := bson.D{}

	StartTime := this.Ctx.GetParam("StartTime")
	EndTime := this.Ctx.GetParam("EndTime")
	if StartTime != "" || EndTime != "" {
		timesBsonD := bson.D{}
		if StartTime != "" {
			startTime, err := time.ParseInLocation("2006-01-02", StartTime, common.LocationTime)
			if err != nil {
				panic(err)
			}
			timesBsonD = append(timesBsonD, bson.E{"$gte", startTime})
		}
		if EndTime != "" {
			endTime, err := time.ParseInLocation("2006-01-02", EndTime, common.LocationTime)
			if err != nil {
				panic(err)
			}
			timesBsonD = append(timesBsonD, bson.E{"$lte", endTime})
		}
		filter = append(filter, bson.E{"Time", timesBsonD})
	}

	allMinerIds := this.MinerAo.GetMinerIds()
	allowsSearchMinerIds := []string{}
	for _, singleAuthority := range client.Authority {
		for _, singleMinerId := range allMinerIds {
			if singleAuthority == singleMinerId {
				allowsSearchMinerIds = append(allowsSearchMinerIds, singleMinerId)
				break
			}
		}
	}

	searchMiner := bson.A{}
	minerId := this.Ctx.GetParam("MinerId")
	if minerId != "" {
		for _, singleMinerId := range allowsSearchMinerIds {
			if minerId == singleMinerId {
				filter = append(filter, bson.E{"MinerId", minerId})
				return this.ExpendAo.Search(filter, opts)
			}
		}
		return expend.Expends{}
	}
	for _, singleMinerId := range allowsSearchMinerIds {
		searchMiner = append(searchMiner, bson.D{{"MinerId", singleMinerId}})
	}

	filter = append(filter, bson.E{"$or", searchMiner})

	return this.ExpendAo.Search(filter, opts)
}

func (this *MinerController) searchIncome(opts *options.FindOptions) income.Incomes {

	client := this.ClientAo.IsLogin()
	filter := bson.D{}

	StartTime := this.Ctx.GetParam("StartTime")
	EndTime := this.Ctx.GetParam("EndTime")
	if StartTime != "" || EndTime != "" {
		timesBsonD := bson.D{}
		if StartTime != "" {
			startTime, err := time.ParseInLocation("2006-01-02", StartTime, common.LocationTime)
			if err != nil {
				startTime, err = time.ParseInLocation("2006-01-02 15:04:05", StartTime, common.LocationTime)
				if err != nil {
					panic(err)
				}
			}
			startTime = time.Date(startTime.Year(), startTime.Month(), startTime.Day(), 0, 0, 0, 0, common.LocationTime)
			timesBsonD = append(timesBsonD, bson.E{"$gte", startTime})
		}
		if EndTime != "" {
			endTime, err := time.ParseInLocation("2006-01-02", EndTime, common.LocationTime)
			if err != nil {
				endTime, err = time.ParseInLocation("2006-01-02 15:04:05", EndTime, common.LocationTime)
				if err != nil {
					panic(err)
				}
			}
			endTime = time.Date(endTime.Year(), endTime.Month(), endTime.Day(), 23, 59, 59, 0, common.LocationTime)
			timesBsonD = append(timesBsonD, bson.E{"$lte", endTime})
		}
		filter = append(filter, bson.E{"Time", timesBsonD})
	}

	allMinerIds := this.MinerAo.GetMinerIds()
	allowsSearchMinerIds := []string{}
	for _, singleAuthority := range client.Authority {
		for _, singleMinerId := range allMinerIds {
			if singleAuthority == singleMinerId {
				allowsSearchMinerIds = append(allowsSearchMinerIds, singleMinerId)
				break
			}
		}
	}

	searchMiner := bson.A{}
	minerId := this.Ctx.GetParam("MinerId")
	if minerId != "" {
		for _, singleMinerId := range allowsSearchMinerIds {
			if minerId == singleMinerId {
				filter = append(filter, bson.E{"MinerId", minerId})
				return this.IncomeAo.Search(filter, opts)
			}
		}
		return income.Incomes{}
	}

	for _, singleMinerId := range allowsSearchMinerIds {
		searchMiner = append(searchMiner, bson.D{{"MinerId", singleMinerId}})
	}

	filter = append(filter, bson.E{"$or", searchMiner})

	return this.IncomeAo.Search(filter, opts)
}

func (this *MinerController) GetByMinerId_Json() interface{} {
	this.ClientAo.IsLoginAndCheckoutAuth(client.ClientAuthorityEnum.AccountManagement)

	minerId := this.Ctx.GetParam("minerId")
	this.Log.Debug("%+v", minerId)

	if minerId == "" {
		language.Throw(1, "minerId不能为空")
	}

	result := this.MinerAo.FindOne(miner.MinerBase{MinerId: minerId}, options.FindOne())
	return result
}

func (this *MinerController) Add_Json() {
	user := this.ClientAo.IsLoginAndCheckoutAuth(client.ClientAuthorityEnum.AccountManagement)
	clientData := this.check()
	if len(clientData.MinerId) > 4 {
		this.Log.Debug("%+v", clientData.MinerId[0:2])
		if clientData.MinerId[0:2] != "f0" {
			language.Throw(1, "矿工号必须f0开头，请重新输入")
		}
	} else {
		language.Throw(1, "矿工号必须大于f099，请重新输入")
	}
	addr, err := address.NewFromString(clientData.MinerId)
	if err != nil {
		panic(err)
	}
	_, err = common.LotusApi.StateMinerInfo(context.TODO(), addr, common.NowTipSet.Key())
	if err != nil {
		this.Log.Error("$+v", err)
		language.Throw(1, "该号码不是矿工")
	}
	clientResult := this.MinerAo.FindOne(clientData, options.FindOne())
	if clientResult.MinerId != "" {
		language.Throw(1, "矿工号已经存在，请重新输入")
	}
	this.MinerAo.Upsert(clientData)
	this.DefineController.AgainSyncOldData()
	this.Log.Notice("管理员%s,$s添加矿工$+v", user.ClientId, user.Name, clientData)
}

func (this *MinerController) Update_Json() {
	user := this.ClientAo.IsLoginAndCheckoutAuth(client.ClientAuthorityEnum.AccountManagement)

	minerData := miner.MinerBase{}
	this.CommonFunc.PostToStruct(&minerData)
	searchData := miner.MinerBase{MinerId: minerData.MinerId}

	if searchData.MinerId == "" {
		language.Throw(1, "用户不存在")
	}
	minerData.MinerId = searchData.MinerId
	this.MinerAo.Upsert(minerData)
	this.Log.Notice("管理员%s,%s更新了矿工%+v", user.ClientId, user.Name, minerData)
}

func (this *MinerController) Del_Json() {
	user := this.ClientAo.IsLoginAndCheckoutAuth(client.ClientAuthorityEnum.AccountManagement)
	clientData := miner.MinerBase{
		MinerId: this.Ctx.GetParam("MinerId"),
	}
	if clientData.MinerId == "" {
		language.Throw(1, "矿工ID不能为空")
	}
	filterData := miner.MinerBase{MinerId: this.Ctx.GetParam("MinerId")}
	this.MinerAo.DeleteOne(filterData)

	this.Log.Notice("管理员%s,%s删除了矿工%+v", user.ClientId, user.Name, clientData)
}

func (this *MinerController) check() miner.MinerBase {
	minerData := miner.MinerBase{}
	this.CommonFunc.PostToStruct(&minerData)

	if minerData.MinerId == "" {
		language.Throw(1, "用户名不能为空")
	}
	return minerData
}

func (this *MinerController) SyncAllRoundWinner_Json() {
	if total.SyncRoundWinnerMap == nil {
		total.SyncRoundWinnerMap = map[string]int64{}
	}
	minerIds := this.MinerAo.GetMinerIds()
	for _, singleMinerId := range minerIds {
		if total.SyncRoundWinnerMap[singleMinerId] > 0 {
			language.Throw(1, "执行失败，有矿工在同步中")
		}
	}
	go func() {
		defer language.CatchCrash(func(exception language.Exception) {
			this.Log.Critical("SyncYesterdayMessagesByFilfox Crash Code:[%d] Message:[%s]\nStackTrace:[%s]", exception.GetCode(), exception.GetMessage(), exception.GetStackTrace())
		})
		defer func() {
			total.SyncRoundWinnerMap = map[string]int64{}
		}()
		this.Log.Debug("SyncYesterdayMessagesByFilfox:%+v", "AddBatchTotalByRightHeight")
		minerIds := this.MinerAo.GetMinerIds()
		for _, singleMinerId := range minerIds {
			total.SyncRoundWinnerMap[singleMinerId] = 0
		}
		this.TotalAo.SyncRoundWinner("")

	}()
}

func (this *MinerController) SyncVestingFunds_Json() {
	go func() {
		defer language.CatchCrash(func(exception language.Exception) {
			this.Log.Critical("SyncVestingFunds Crash Code:[%d] Message:[%s]\nStackTrace:[%s]", exception.GetCode(), exception.GetMessage(), exception.GetStackTrace())
		})
		this.Log.Debug("SyncVestingFunds:%+v", "AddBatchTotalByRightHeight")
		this.TotalAo.SyncVestingFunds("", 0)
	}()
}

func (this *MinerController) SyncSingleMinerIdRoundWinner_Json() {
	if total.SyncRoundWinnerMap == nil {
		total.SyncRoundWinnerMap = map[string]int64{}
	}
	minerId := this.Ctx.GetParam("MinerId")
	if total.SyncRoundWinnerMap[minerId] > 0 {
		language.Throw(1, "执行失败，该矿工在同步中")
	}
	go func(minerId string) {
		defer language.CatchCrash(func(exception language.Exception) {
			this.Log.Critical("SyncYesterdayMessagesByFilfox Crash Code:[%d] Message:[%s]\nStackTrace:[%s]", exception.GetCode(), exception.GetMessage(), exception.GetStackTrace())
		})
		defer func(minerId string) {
			delete(total.SyncRoundWinnerMap, minerId)
		}(minerId)
		this.Log.Debug("SyncYesterdayMessagesByFilfox:%+v", "AddBatchTotalByRightHeight")
		total.SyncRoundWinnerMap[minerId] = 0
		this.TotalAo.SyncRoundWinner(minerId)
	}(minerId)
}

func (this *MinerController) Test_Json() {
	addr := this.MinerAo.GetMinerAddress()["f07830"]
	this.Log.Debug("%+v", addr.String())
	//nowHeight := int64(common.NowTipSet.Height())
	a, err := common.LotusApi.StateReadState(context.TODO(), addr, common.NowTipSet.Key())
	if err != nil {
		panic(err)
	}
	this.Log.Debug("%+v", a)
}
